package org.jflame.devAide.component;

import java.util.Optional;
import java.util.concurrent.TimeUnit;

import org.jflame.commons.json.JsonHelper;
import org.jflame.devAide.AppContext;
import org.jflame.devAide.component.EditorCfg.ToolBarStyle;
import org.jflame.devAide.util.UIUtils;
import org.jflame.fx.control.JFLAlert;

import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.concurrent.Worker;
import javafx.concurrent.Worker.State;
import javafx.event.EventHandler;
import javafx.geometry.HPos;
import javafx.geometry.VPos;
import javafx.scene.control.ButtonType;
import javafx.scene.layout.Region;
import javafx.scene.web.WebEngine;
import javafx.scene.web.WebErrorEvent;
import javafx.scene.web.WebEvent;
import javafx.scene.web.WebView;
import javafx.util.Callback;
import netscape.javascript.JSException;
import netscape.javascript.JSObject;

/**
 * html富文本编辑器
 * 
 * @author charles.zhang
 */
public class WebEditor extends Region {

    private final WebView webView;
    private final WebEngine webEngine;
    private EditorJsBridge jsBrigde;
    private EditorJavaBrigde javaBrigde = new EditorJavaBrigde();

    private SimpleBooleanProperty valueChangedProperty = new SimpleBooleanProperty();

    public WebEditor() {
        this(ToolBarStyle.FULL, null);
    }

    public WebEditor(String content) {
        this(ToolBarStyle.FULL, content);
    }

    public WebEditor(ToolBarStyle toolBarStyle) {
        this(ToolBarStyle.SIMPLE, null);
    }

    public WebEditor(ToolBarStyle toolBarStyle, String content) {
        webView = new WebView();
        webView.setContextMenuEnabled(true);
        webEngine = webView.getEngine();

        getChildren().add(webView);
        // 界面调整时,调整html editor的高度
        heightProperty().addListener(new ChangeListener<Number>() {

            @Override
            public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) {
                if (jsBrigde != null) {
                    System.out.println("o:" + oldValue + ",new:" + newValue);
                    jsBrigde.setHeight(newValue.intValue());
                }
            }
        });
        // js alert处理
        webEngine.setOnAlert(new EventHandler<WebEvent<String>>() {

            @Override
            public void handle(WebEvent<String> event) {
                if (event.getData() != null) {
                    UIUtils.info(event.getData());
                }
            }
        });

        // js confirm处理
        webEngine.setConfirmHandler(new Callback<String,Boolean>() {

            @Override
            public Boolean call(String param) {
                Optional<ButtonType> result = JFLAlert.confirm(param, getScene().getWindow());
                ButtonType resultType = result.orElse(ButtonType.CANCEL);
                return resultType == ButtonType.OK;
            }
        });

        // 错误处理
        webEngine.setOnError(new EventHandler<WebErrorEvent>() {

            @Override
            public void handle(WebErrorEvent event) {
                System.err.println("error:" + event.getMessage());
            }
        });

        EditorCfg editorCfg = new EditorCfg();
        if (toolBarStyle != null) {
            editorCfg.toolBarStyle(toolBarStyle);
        }

        editorCfg.setLang(AppContext.getLocale()
                .toLanguageTag()
                .toLowerCase());
        // 加载html editor
        webEngine.getLoadWorker()
                .stateProperty()
                .addListener(new ChangeListener<State>() {

                    @Override
                    public void changed(ObservableValue<? extends State> ov, State oldState, State newState) {
                        if (newState == Worker.State.SUCCEEDED) {
                            try {
                                JSObject window = (JSObject) webEngine.executeScript("window");
                                window.setMember("javaBridge", javaBrigde);
                                // window.setMember("console", javaBrigde);// js console转到system.out,不调试时可注销

                                editorCfg.setInitHeight((int) webView.getHeight());

                                JSObject jsObj = (JSObject) webEngine.executeScript("getJsBridge()");
                                jsBrigde = new EditorJsBridge(jsObj);
                                jsBrigde.initEditor(editorCfg, content);
                            } catch (JSException e) {
                                e.printStackTrace();
                            }
                        }
                    }
                });

        webEngine.load(WebEditor.class.getResource("/editor/editor.html")
                .toExternalForm());
    }

    /**
     * 获取编辑器内容
     * 
     * @return
     */
    public String getText() {
        if (jsBrigde != null) {
            return jsBrigde.getContent();
        }
        return null;
    }

    /**
     * 设置编辑器内容.
     * <p>
     * 调用该方法时编辑器可能未初始完成,方法会循环调用直到超时(3秒)
     * 
     * @param content
     */
    public void setText(String content) {
        int i = 0;
        do {
            if (jsBrigde != null && jsBrigde.isReady()) {
                jsBrigde.setContent(content);
                break;
            }
            try {
                TimeUnit.MILLISECONDS.sleep(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            i++;
        } while (i < 60);
    }

    /**
     * 编辑器内容是否被改变属性
     * 
     * @return
     */
    public SimpleBooleanProperty valueChangedProperty() {
        return valueChangedProperty;
    }

    public boolean valueChanged() {
        return valueChangedProperty.get();
    }

    @Override
    protected void layoutChildren() {
        double w = getWidth();
        double h = getHeight();
        layoutInArea(webView, 0, 0, w, h, 0, HPos.CENTER, VPos.CENTER);
    }

    /**
     * html editor中提供的js方法代理
     * 
     * @author charles.zhang
     */
    @SuppressWarnings("unused")
    private class EditorJsBridge {

        private JSObject jsObj;

        public EditorJsBridge(JSObject _jsObj) {
            jsObj = _jsObj;
        }

        public void initEditor(EditorCfg cfg, String content) {
            // 直传对象好像接收不了,转为json传
            jsObj.call("init", JsonHelper.toJson(cfg), content);
        }

        public void initEditor(EditorCfg cfg) {
            initEditor(cfg, "");
        }

        public void setContent(String html) {
            jsObj.call("setContent", html);
        }

        public void setHeight(int height) {
            jsObj.call("setHeight", height);
        }

        public void focus() {
            jsObj.call("focus");
        }

        public String getContent() {
            String content = (String) jsObj.call("getContent");
            return content;
        }

        /**
         * 判断编辑器是否初始化完成
         */
        public boolean isReady() {
            return (Boolean) jsObj.call("isReady");
        }

    }

    /**
     * 提供方法给html editor中js调用
     * 
     * @author charles.zhang
     */
    public class EditorJavaBrigde {

        public void log(String text) {
            System.out.println("js out:" + text);
        }

        public void error(String error) {
            System.err.println(error);
        }

        public void valueChanged() {
            /* if (!valueChangedProperty.get()) {
                valueChangedProperty.setValue(true);
            }*/
        }
    }

}
